#include "stdafx.h"
#include "ListItemDetail.h"
#include "GraphicFunctions.h"
#include "Settings.h"
#include "PoomPictureLoader.h"
#include "PoomData.h"
#include "PhoneUtils.h"
#include "MenuBar.h"
#include "PictureSaver.h"
#include "Language.h"

#ifdef _DEBUG
#include "crtdbg.h"
#endif

/////////////////////////////////////
//  CListItemDetail
/////////////////////////////////////

bool CListItemDetail::OnMouseClick(HWND hParent, int x, int y)
{
  //PostCommand(hParent, CMD_DETAILS_BACK);
  return true;
}

bool CListItemDetail::OnKeyUp(HWND hParent, int vKey)
{
  //PostCommand(hParent, CMD_DETAILS_BACK);
  return true;
}

void CListItemDetail::Draw(HDC hdc, const RECT& rc) const
{
  DrawBackground(hdc, rc, this->GetHovered());
  DrawDetailsIcon(hdc, rc, this->type);
}

void CListItemDetail::DrawBackground(HDC hdc, const RECT& rc, bool selected) const
{
  RECT rItem = rc;
  // separator
  rItem.bottom = rItem.top + 1;
  FillRect(hdc, &rItem, Theme::hbrDetailItemSeparator);

  rItem.top = rItem.bottom;
  rItem.bottom = rItem.top + this->height - 1;

  // background of selected item
  if (selected)
  {
    GraphicFunctions::DrawGradientGDI(hdc, rItem, Theme::rgbDetailsHoverBackground1, Theme::rgbDetailsHoverBackground2);
  }
}

void CListItemDetail::DrawDetailsIcon(HDC hdc, const RECT& rc, ItemPropertyType forType) const
{
  RECT rItem = rc;
  // separator
  rItem.top++;
  rItem.bottom = rItem.top + this->height - 1;

  // Draw Item Icon
  Theme::DrawDetailsIcon(hdc,
      Theme::iDetailsIndent + (Theme::iDetailsTypeWidth - Theme::iDetailIconsW) / 2,
      rItem.top + (Theme::iDetailsItemSize - Theme::iDetailIconsH) / 2,
      GetDetailsIcon(forType));
}

EDetailsIcon CListItemDetail::GetDetailsIcon(ItemPropertyType eType) const
{
  switch(eType)
  {
  case iptMobileNumber:
  case iptTelephoneNumber:
    return diMobileNumber;
  case iptHomeNumber:
  case iptHomeNumber2:
    return diHomeNumber;
  case iptBusinessNumber:
  case iptBusinessNumber2:
    return diBusinessNumber;
  case iptCarNumber:
    return diCarNumber;
  case iptRadioNumber:
    return diRadioNumber;
  case iptHomeFax:
    return diHomeFax;
  case iptBusinessFax:
    return diBusinessFax;
  case iptPager:
    return diPager;
  case iptEmail:
  case iptEmail2:
  case iptEmail3:
    return diEmail;
  case iptWebPage:
    return diWebPage;
  case iptHomeAddress:
    return diHomeAddress;
  case iptBusinessAddress:
    return diBusinessAddress;
  case iptOtherAddress:
    return diBusinessAddress;
  case iptCallType:
    return diCallType;
  case iptDate:
    return diDate;
  case iptDuration:
    return diDuration;
  case iptRepeated:
    return diRepeated;
  case iptNotes:
    return diNotes;
  case iptCompanyNumber:
    return diCompanyNumber;
  case iptInstantMessenger:
    return diInstantMessenger;
  case iptNickname:
    return diNickname;
  case iptCategories:
    return diCategories;
  case iptAnniversary:
    return diAnniversary;
  case iptBirthday:
    return diBirthday;
  default:
    return diNone;
  }
}

void CListItemDetail::CropString(TCHAR* str, int maxLen, HDC hdc, int nWidth)
{
  // Crop the string
  if (nWidth == 0)
    return;

  int len = (int)_tcslen(str);
  if (len == 0)
    return;

  int* charWidths = new int[len];
  SIZE size;
  if (::GetTextExtentExPoint(hdc, str, len, 0, NULL, charWidths, &size) && size.cx > nWidth)
  {
    int pos = 0;
    for (; pos < len; pos++)
    {
      if (charWidths[pos] >= nWidth)
        break;
    }
    if (pos > 3)
    {
      str[pos-3] = 0;
      StringCchCat(str, maxLen, TEXT("..."));
    }
  }
  delete[] charWidths;
}

/////////////////////////////////////
//  CListItemDetailHeader
/////////////////////////////////////

CListItemDetailHeader::CListItemDetailHeader(const TCHAR* sName, const TCHAR* sCompany,
                 const TCHAR* sDepartment, const TCHAR* sPosition,
                 const TCHAR* sBirthday,  const TCHAR* sAnniversary,
                 CEOID nItemOid, ESpecialIcons eNoPic)
  : CListItemDetail(iptNone), picture(NULL), pictureW(0), pictureH(0)
{
  if (sName)    ::StringCchCopy(name, PRIMARY_TEXT_LENGTH, sName);
  else            name[0] = 0;
  name2[0] = 0;
  if (sCompany) ::StringCchCopy(company, PRIMARY_TEXT_LENGTH, sCompany);
  else            company[0] = 0;
  if (sDepartment) ::StringCchCopy(department, PRIMARY_TEXT_LENGTH, sDepartment);
  else            department[0] = 0;
  if (sPosition)::StringCchCopy(position, PRIMARY_TEXT_LENGTH, sPosition);
  else            position[0] = 0;
  if (sBirthday)::StringCchCopy(birthday, PRIMARY_TEXT_LENGTH, sBirthday);
  else            birthday[0] = 0;
  if (Theme::HasSmallIcons && sAnniversary)::StringCchCopy(anniversary, PRIMARY_TEXT_LENGTH, sAnniversary);
  else            anniversary[0] = 0;
  itemOid = nItemOid;
  noPicIcon = eNoPic;
}

CListItemDetailHeader::~CListItemDetailHeader()
{
  if (picture != NULL)
    ::DeleteObject(picture);
}

void CListItemDetailHeader::Clear()
{
  if (picture != NULL)
    ::DeleteObject(picture);
  picture = NULL;

  CListItemDetail::Clear();
}

void CListItemDetailHeader::DrawDetailText(HDC hdc, TCHAR* text, RECT* r, EDetailsIcon icon)
{
  RECT rText; 
  if (text[0] != 0)
  {
    //rText = (RECT)&r;
    SetRect(&rText, r->left, r->top, r->right, r->bottom);
    bool HasIcon = (icon != diNone) && Theme::HasSmallIcons && Theme::IsSmallIcon(icon);
    rText.bottom = rText.top + GetDetailTextHeight(HasIcon);
    if (HasIcon) 
    {                                                                                                                         
      Theme::DrawDetailsIcon(hdc, r->left, r->top, icon);
      rText.left += Theme::iDetailSmallIconsW + 2;
      DrawText(hdc, text, -1, &rText, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
    }
    else
      ExtTextOut(hdc, rText.left, rText.top, 0, NULL, text, _tcslen(text), 0);
    r->top += GetDetailTextHeight(HasIcon);
  }
}

int CListItemDetailHeader::GetDetailTextHeight(bool hasIcon)
{
  int h = Theme::DetailsHeaderFont2Size + Theme::iDetailsHeaderSpacing;
  if (hasIcon && Theme::HasSmallIcons)
    if (h < Theme::iDetailSmallIconsH + 2)
      h = Theme::iDetailSmallIconsH + 2;
  return h;
}

void CListItemDetailHeader::PrepareDetailText(HDC hdc, TCHAR* text, int textWidth, EDetailsIcon icon)
{
  bool hasIcon = (icon != diNone) && Theme::HasSmallIcons;
  int iconWidth = 0;
  if (hasIcon) 
    iconWidth += Theme::iDetailSmallIconsW + 2;
  if (text[0] != 0)
  {
    height += GetDetailTextHeight(true);
    CropString(text, PRIMARY_TEXT_LENGTH, hdc, textWidth - iconWidth);
  }
}

void CListItemDetailHeader::Prepare(HDC hdc, int w, int h)
{
  if (picture != NULL)
  {
    ::DeleteObject(picture);
    picture = NULL;
  }

  CListItemDetail::Prepare(hdc, w, h);

  // Calculate header height
  height = Theme::iDetailsIndent*2 + Theme::DetailsHeaderFontSize;

  // prepare picture
  if (Settings::showPicturesInDetails)
  {
    bool hasPicture = false;
    pictureW = Theme::iDetailsPictureWidth;
    if (itemOid != 0)
    {
      pictureH = Theme::iDetailsHeaderSize - Theme::iDetailsIndent*2;
      hasPicture = PoomPictureLoader::GetContactPicture(PoomData::GetApp(), itemOid, hdc,
                              picture, pictureW, pictureH);
    }
    if (!hasPicture)
    { // prepare empty picture
      pictureW = Theme::SpecialIconsWidth;
      pictureH = Theme::SpecialIconsHeight;
    }
    if (pictureH > Theme::iDetailsHeaderSize - Theme::iDetailsIndent*2)
      pictureH = Theme::iDetailsHeaderSize - Theme::iDetailsIndent*2;
  }
  else
  {
    pictureW = 0;
    pictureH = 0;
  }

  // check if name should be splitted
  int  textWidth = width - Theme::iDetailsIndent*2 - pictureW;
  SIZE sz;
  SelectObject(hdc, Theme::DetailsHeaderFont);
  GetTextExtentPoint(hdc, name, _tcslen(name), &sz);
  if (sz.cx > textWidth)
  {
    // split name
    TCHAR* str = _tcschr(name, TEXT(','));
    if (str != NULL)
    {
      str[0] = 0;
      do
      {
        str++;
      }
      while (*str != 0 && _istspace(*str));
      StringCchCopy(name2, PRIMARY_TEXT_LENGTH, str);
    }  
    if (name2[0] != 0)
      height += Theme::DetailsHeaderFontSize + Theme::iDetailsHeaderSpacing;
    // adjust name
    CropString(name,  PRIMARY_TEXT_LENGTH, hdc, textWidth);
    CropString(name2, PRIMARY_TEXT_LENGTH, hdc, textWidth);
  }

  // prepare and adjust company, position, birthday and anniversary
  SelectObject(hdc, Theme::DetailsHeaderFont2);
  PrepareDetailText(hdc, (TCHAR*)&company, textWidth, diNone);
  PrepareDetailText(hdc, (TCHAR*)&department, textWidth, diNone);
  PrepareDetailText(hdc, (TCHAR*)&position, textWidth, diNone);
  PrepareDetailText(hdc, (TCHAR*)&birthday, textWidth, diBirthday);
  PrepareDetailText(hdc, (TCHAR*)&anniversary, textWidth, diAnniversary);

  if (pictureH + Theme::iDetailsIndent*2 > height)
    height = pictureH + Theme::iDetailsIndent*2;
}

void CListItemDetailHeader::Draw(HDC hdc, const RECT& rc) const
{
  RECT rItem = rc;

  // background of selected item
  if (this->GetHovered())
  {
    GraphicFunctions::DrawGradientGDI(hdc, rItem, Theme::rgbDetailsHoverBackground1, Theme::rgbDetailsHoverBackground2);
  }

  rItem.left += Theme::iDetailsIndent;
  rItem.top += Theme::iDetailsIndent;

  // Contact Picture
  if (Settings::showPicturesInDetails)
  {
    if (picture != NULL)
    { // draw picture, centered
      Theme::hbmTemp = (HBITMAP)SelectObject(Theme::hdcTemp, picture);
      if (Settings::enablePicturesTransparency)
      {
        COLORREF transpCol = GetPixel(Theme::hdcTemp, 0, 0);
        TransparentBlt(hdc,
          rItem.left + (Theme::iDetailsPictureWidth - pictureW) / 2,
          rItem.top + (height - Theme::iDetailsIndent - pictureH) / 2,
          pictureW, pictureH, Theme::hdcTemp, 0, 0, pictureW, pictureH,
          transpCol);
      }
      else
      {
        BitBlt(hdc,
          rItem.left + (Theme::iDetailsPictureWidth - pictureW) / 2,
          rItem.top + (height - Theme::iDetailsIndent - pictureH) / 2,
          pictureW, pictureH, Theme::hdcTemp, 0, 0, SRCCOPY);
      }
      SelectObject(Theme::hdcTemp, Theme::hbmTemp);
    }
    else
    {
      Theme::DrawSpecialIcon(hdc,
        rItem.left + (Theme::iDetailsPictureWidth - pictureW) / 2,
        rItem.top + (height - Theme::iDetailsIndent - pictureH) / 2,
        noPicIcon);
    }
    rItem.left += Theme::iDetailsPictureWidth + Theme::iDetailsIndent;
  }

  // Contact Name
  // contact name 1
  SelectObject(hdc, Theme::DetailsHeaderFont);
  SetTextColor(hdc, Theme::rgbDetailsHeaderShadow);
  ExtTextOut(hdc, rItem.left, rItem.top + 1, 0, NULL, name, _tcslen(name), 0);
  SetTextColor(hdc, Theme::rgbDetailsHeaderText);
  ExtTextOut(hdc, rItem.left, rItem.top, 0, NULL, name, _tcslen(name), 0);
  rItem.top += Theme::DetailsHeaderFontSize + Theme::iDetailsHeaderSpacing;
  // contact name 2?
  if (name2[0] != 0)
  {
    SetTextColor(hdc, Theme::rgbDetailsHeaderShadow);
    ExtTextOut(hdc, rItem.left, rItem.top + 1, 0, NULL, name2, _tcslen(name2), 0);
    SetTextColor(hdc, Theme::rgbDetailsHeaderText);
    ExtTextOut(hdc, rItem.left, rItem.top, 0, NULL, name2, _tcslen(name2), 0);
    rItem.top += Theme::DetailsHeaderFontSize + Theme::iDetailsHeaderSpacing;
  }

  // Contact Company, Position, Birthday and Anniversary
  SelectObject(hdc, Theme::DetailsHeaderFont2);
  DrawDetailText(hdc, (TCHAR *)&company, &rItem, diNone);
  DrawDetailText(hdc, (TCHAR *)&department, &rItem, diNone);
  DrawDetailText(hdc, (TCHAR *)&position, &rItem, diNone);
  DrawDetailText(hdc, (TCHAR *)&birthday, &rItem, diBirthday);
  DrawDetailText(hdc, (TCHAR *)&anniversary, &rItem, diAnniversary);
}

bool CListItemDetailHeader::OnMouseClick(HWND hParent, int x, int y)
{
  // Contact Picture
  if (Settings::showPicturesInDetails)
  {
    if (x > Theme::iDetailsIndent && x < Theme::iDetailsIndent + Theme::iDetailsPictureWidth &&
        y > Theme::iDetailsIndent && y < height - Theme::iDetailsIndent)
    {
      // click on picture
      PostCommand(hParent, CMD_DETAILS_SET_PICTURE);
    }
  }
  return true;
}

bool CListItemDetailHeader::OnKeyUp(HWND hParent, int vKey)
{
  switch(vKey)
  {
  case VK_TACTION:
    PostCommand(hParent, CMD_DETAILS_SET_PICTURE);
    return true;
  }

  return CListItemDetail::OnKeyUp(hParent, vKey);
}

/////////////////////////////////////
//  CListItemDetailString
/////////////////////////////////////

CListItemDetailString::CListItemDetailString(ItemPropertyType eType, const TCHAR* sString)
  : CListItemDetail(eType)
{
  if (sString)
    ::StringCchCopy(this->string, PRIMARY_TEXT_LENGTH, sString);
  else 
    this->string[0] = 0;
  this->stringLen = _tcslen(this->string);
}

void CListItemDetailString::Prepare(HDC hdc, int w, int h)
{
  CListItemDetail::Prepare(hdc, w, h);

  SelectObject(hdc, Theme::DetailsItemFont);
  CropString(this->string, PRIMARY_TEXT_LENGTH, hdc, this->width - Theme::iDetailsIndent*2 - Theme::iDetailsTypeWidth);
  this->stringLen = _tcslen(this->string);
}

void CListItemDetailString::Draw(HDC hdc, const RECT& rc) const
{
  CListItemDetail::Draw(hdc, rc);

  RECT rItem = rc;
  SelectObject(hdc, Theme::DetailsItemFont);
  // Draw String
  rItem.left += Theme::iDetailsTypeWidth + Theme::iDetailsIndent + 3;
  rItem.top += (height - Theme::DetailsItemFontSize) / 2 + 1;
  if (this->GetHovered())
  { // shadow for selected item
    ::SetTextColor(hdc, Theme::rgbDetailsItemShadow);
    ExtTextOut(hdc, rItem.left, rItem.top - 1, 0, NULL, this->string, this->stringLen, 0);
  }
  ::SetTextColor(hdc, Theme::rgbDetailsItemText);
  ExtTextOut(hdc, rItem.left, rItem.top, 0, NULL, this->string, this->stringLen, 0);
}

/////////////////////////////////////
//  CListItemDetailAction
/////////////////////////////////////

CListItemDetailAction::CListItemDetailAction(ItemPropertyType eType,
                 const TCHAR* sName, const TCHAR* sData,
                 ListItemAction eAction, ListItemAction eSubAction,
                 bool bIsDefault, CEOID nItemId, CEPROPID nPropId)
  : CListItemDetail(eType), name(sName), action(eAction), subAction(eSubAction),
    isDefault(bIsDefault), itemId(nItemId), propId(nPropId), isSubAction(false)
{
  if (sData)
  {
    ::StringCchCopy(label, PRIMARY_TEXT_LENGTH, sData);
    ::StringCchCopy(data, PRIMARY_TEXT_LENGTH, sData);
  }
  else
  {
    label[0] = 0;
    data[0] = 0;
  }
  this->labelLen = _tcslen(this->label);
}

void CListItemDetailAction::Prepare(HDC hdc, int w, int h)
{
  CListItemDetail::Prepare(hdc, w, h);

  SelectObject(hdc, Theme::DetailsItemFont);
  w -= Theme::iDetailsIndent*2 + Theme::iDetailsTypeWidth;
  if (this->subAction != liaNone)
    w -= Theme::iDetailsSubActionWidth;
  CropString(this->label, PRIMARY_TEXT_LENGTH, hdc, w);
  this->labelLen = _tcslen(this->label);
}

void CListItemDetailAction::Draw(HDC hdc, const RECT& rc) const
{
  RECT rItem = rc;

  // avoid highlighting full item when only sub action is hl
  if (this->isSubAction)
  {
    DrawBackground(hdc, rc, false);
    DrawDetailsIcon(hdc, rc, this->type);

    rItem.right = rItem.right - Theme::iListScrollbarWidth;
    rItem.left = rItem.right - Theme::iDetailsSubActionWidth;
    GraphicFunctions::DrawGradientGDI(hdc, rItem, Theme::rgbDetailsHoverBackground1, Theme::rgbDetailsHoverBackground2);
    rItem.right = rc.right;
    rItem.left = rc.left;
  }
  else
  {
    CListItemDetail::Draw(hdc, rc);
  }

  // Draw Highlight Icon
  if (this->isDefault)
  {
    Theme::DrawSpecialIcon(hdc,
      rItem.left + (Theme::iDetailsTypeWidth - Theme::iDetailIconsW) / 2,
      rItem.top + (Theme::iDetailsItemSize - Theme::iDetailIconsH) / 2,
      siSelected);
  }

  if (this->subAction != liaNone)
  {
    ESpecialIcons subIcon = this->subAction == liaSms ? siSmsAction : siNone;
    // Draw SubAction Icon
    Theme::DrawSpecialIcon(hdc,
      rItem.right - (Theme::iListScrollbarWidth + ((Theme::iDetailsSubActionWidth + Theme::SpecialIconsWidth) / 2)),
      rItem.top + (Theme::iDetailsItemSize - Theme::SpecialIconsHeight) / 2,
      subIcon);
  }

  // Draw Label
  SelectObject(hdc, Theme::DetailsItemFont);
  rItem.left += Theme::iDetailsTypeWidth + Theme::iDetailsIndent + 3;
  rItem.top += (height - Theme::DetailsItemFontSize) / 2 + 1;
  if (this->GetHovered())
  { // shadow for selected item
    ::SetTextColor(hdc, Theme::rgbDetailsItemShadow);
    ExtTextOut(hdc, rItem.left, rItem.top - 1, 0, NULL, this->label, this->labelLen, 0);
  }
  // color is different for default item
  if (this->isDefault)
    ::SetTextColor(hdc, Theme::rgbDetailsDefaultNumber);
  else
    ::SetTextColor(hdc, Theme::rgbDetailsItemText);
  ExtTextOut(hdc, rItem.left, rItem.top, 0, NULL, this->label, this->labelLen, 0);
}

void CListItemDetailAction::OnMouseOver(int x, int y)
{
  if (x < width - Theme::iListScrollbarWidth)
  {
    CListItemDetail::OnMouseOver(x, y);
    isSubAction = subAction != liaNone && (x > width - (Theme::iDetailsSubActionWidth + Theme::iListScrollbarWidth));
  }
}

void CListItemDetailAction::OnMouseMove(int x, int y)
{
  if (x < width - Theme::iListScrollbarWidth)
  {
    CListItemDetail::OnMouseMove(x, y);
    isSubAction = subAction != liaNone && (x > width - (Theme::iDetailsSubActionWidth + Theme::iListScrollbarWidth));
  }
}

void CListItemDetailAction::OnMouseOut()
{
  CListItemDetail::OnMouseOut();
  isSubAction = false;
}

bool CListItemDetailAction::OnMouseClick(HWND hParent, int x, int y)
{
  bool bIsSubAction = subAction != liaNone && (x > width - (Theme::iDetailsSubActionWidth + Theme::iListScrollbarWidth)) && (x < width - Theme::iListScrollbarWidth);
  if (x < width - Theme::iListScrollbarWidth)
  {
    return DoAction(hParent, bIsSubAction ? subAction : action);
  }
  else
  {
    return false;
  }
}

bool CListItemDetailAction::IsLongTapValid(HWND hParent)
{
  return (action == liaCall && Settings::smartDialingNumber);
}

bool CListItemDetailAction::DoAction(HWND hParent, ListItemAction eAction)
{
  bool result = false;
  switch(eAction)
  {
  case liaCall:
    PhoneUtils::Call(data, name ? name : data);
    result = true;
    break;
  case liaSms:
    PhoneUtils::SendSMS(data);
    result = true;
    break;
  case liaEmail:
    PhoneUtils::SendEMail(Settings::email_account, data);
    result = true;
    break;
  case liaUrl:
    PhoneUtils::OpenURL(data);
    result = true;
    break;
  }

  if (result)
  {
    if (Settings::doExitOnAction)
    {
      DestroyWindow(hParent);
    }
    else if (Settings::doMinimizeOnAction)
    {
      SetWindowPos(hParent, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
    }
  }

  return result;
}

bool CListItemDetailAction::OnKeyUp(HWND hParent, int vKey)
{
  switch(vKey)
  {
  case VK_RIGHT:
    if (subAction != liaNone)
      return DoAction(hParent, subAction);
    break;

  case VK_TACTION:
    return DoAction(hParent, action);
  }

  return CListItemDetail::OnKeyUp(hParent, vKey);
}

bool CListItemDetailAction::OnLongTap(HWND hParent)
{
  if (!Settings::smartDialingNumber)
    return false;

  if (this->itemId != 0 && this->action == liaCall && this->propId != 0)
  {
    HRESULT hr;
    IDispatch* pDisp = NULL;
    IItem* pItem = NULL;
    if (FAILED(PoomData::GetApp()->GetItemFromOid(this->itemId, &pDisp)))
      return false;
    if (FAILED(pDisp->QueryInterface(IID_IItem, (LPVOID*)&pItem)))
    {
      pDisp->Release();
      return false;
    }

    hr = E_FAIL;
    CEPROPVAL propVal;
    propVal.propid = PIMPR_SMARTPROP;
    propVal.wLenData = 0;
    propVal.wFlags = 0;
    propVal.val.ulVal = this->propId;
    if (PoomData::SetItemProperty(pItem, propVal))
    {
      hr = pItem->Save();
    }
    pItem->Release();
    pDisp->Release();
    // details will be reloaded per notification
    return SUCCEEDED(hr);
  }
  return false;
}

/////////////////////////////////////
//  CListItemDetailText
/////////////////////////////////////

CListItemDetailText::CListItemDetailText(ItemPropertyType eType, const TCHAR* sText)
  : CListItemDetail(eType)
{
  lines = NULL;
  count = 0;

  if (!sText)
    return;
  int len = _tcslen(sText);
  if (len == 0)
    return;

  for (int i = 0; i <= (int)len; i++)
  {
    if (i == len || sText[i] == 0 ||
        (sText[i] == TEXT('\r') && sText[i+1] == TEXT('\n')))
      count++;
  }

  if (count == 0)
    return;

  lines = new TCHAR*[count];
  if (lines == NULL)
  {
    count = 0;
    return;
  }

  int idx = 0;
  int last = 0;
  for (int i = 0; i <= (int)len; i++)
  {
    if (i == len || sText[i] == 0 ||
        (sText[i] == TEXT('\r') && sText[i+1] == TEXT('\n')))
    {
      if (sText[last] == TEXT('\n'))
        last++;
      if (i == last)
      {
        lines[idx] = new TCHAR[2];
        StringCchCopy(lines[idx], 2, TEXT(" "));
      }
      else
      {
        lines[idx] = new TCHAR[i - last + 1];
        StringCchCopyN(lines[idx], i - last + 1, sText + last, i - last);
      }
      idx++;
      if (idx >= count)
        break;
      last = i + 1;
    }
  }
}

CListItemDetailText::~CListItemDetailText()
{
  for (int i = 0; i < count; i++)
  {
    if (lines[i])
      delete[] lines[i];
  }
  if (lines)
    delete[] lines;
}

void CListItemDetailText::Clear()
{
  for (int i = 0; i < count; i++)
  {
    if (lines[i])
      delete[] lines[i];
  }
  if (lines)
    delete[] lines;
  lines = NULL;
  count = 0;

  CListItemDetail::Clear();
}

void CListItemDetailText::Prepare(HDC hdc, int w, int h)
{
  CListItemDetail::Prepare(hdc, w, h);

  SelectObject(hdc, Theme::DetailsFooterFont);
  w -= Theme::iDetailsIndent*2 + Theme::iDetailsTypeWidth;
  CropLines(hdc, w);

  height = Theme::iDetailsFooterSpacing + count * (Theme::DetailsFooterFontSize + Theme::iDetailsFooterSpacing) + 1;

  if (height < Theme::iDetailsItemSize)
    height = Theme::iDetailsItemSize;
}

void CListItemDetailText::CropLines(HDC hdc, int nWidth)
{
  if (count <= 0 || nWidth <= 0)
    return;

  bool* wrapArray = new bool[count];
  if (!wrapArray)
    return;

  int numLines = 0;

  int maxLen = 0;
  SIZE size;
  for (int i = 0; i < count; i++)
  {
    int len = (int)_tcslen(lines[i]);
    if (len == 0)
      continue;

    if (len > maxLen)
      maxLen = len;

    ::GetTextExtentPoint(hdc, lines[i], len, &size);
    numLines += size.cx / nWidth + 1;
    wrapArray[i] = (size.cx > nWidth);
  }
  if (maxLen == 0 || numLines <= count)
  {
    delete[] wrapArray;
    return;
  }

  int* charWidths = new int[maxLen];
  if (!charWidths)
  {
    delete[] wrapArray;
    return;
  }
  TCHAR** newLines = new TCHAR*[numLines];
  if (!newLines)
  {
    delete[] wrapArray;
    delete[] charWidths;
    return;
  }

  int idx = 0;
  for (int i = 0; i < count; i++)
  {
    int len = (int)_tcslen(lines[i]);
    if (len == 0)
      continue;

    if (wrapArray[i])
    {
      ::GetTextExtentExPoint(hdc, lines[i], len, 0, NULL, charWidths, &size);
      TCHAR* linePtrLast = lines[i];
      int tmpWidth = nWidth;
      for (TCHAR* linePtr = lines[i];; linePtr++)
      {
        if (*linePtr == 0 || charWidths[linePtr - lines[i]] >= tmpWidth)
        {
          int lineLen = linePtr - linePtrLast;
          newLines[idx] = new TCHAR[lineLen + 1];
          if (!newLines[idx])
            throw TEXT("Not enough memory!");
          StringCchCopyN(newLines[idx], lineLen+1, linePtrLast, lineLen);
          idx++;
          linePtrLast = linePtr;
          tmpWidth += nWidth;
        }
        if (*linePtr == 0)
          break;
      }
    }
    else
    {
      newLines[idx] = new TCHAR[len + 1];
      if (!newLines[idx])
        throw TEXT("Not enough memory!");
      StringCchCopy(newLines[idx], len + 1, lines[i]);
      idx++;
    }
  }
  delete[] charWidths;
  delete[] wrapArray;

  for (int i = 0; i < count; i++)
  {
    if (lines[i])
      delete[] lines[i];
  }
  if (lines)
    delete[] lines;

  lines = newLines;
  count = numLines;
}

void CListItemDetailText::Draw(HDC hdc, const RECT& rc) const
{
  CListItemDetail::Draw(hdc, rc);

  RECT rItem = rc;
  ::SelectObject(hdc, Theme::DetailsFooterFont);
  ::SetTextColor(hdc, Theme::rgbDetailsItemText);

  // Set rect
  rItem.top += Theme::iDetailsFooterSpacing;
  int actualHeight = count * (Theme::DetailsFooterFontSize + Theme::iDetailsFooterSpacing);
  // center text vertically if it fits item size
  if (actualHeight < height)
    rItem.top += (height - actualHeight) / 2;
  rItem.left += Theme::iDetailsTypeWidth + Theme::iDetailsIndent + 3;

  // draw lines of text
  for(int n = 0; n < count; n++)
  {
    const TCHAR* itemStr = lines[n];
    if (itemStr[0] == 0)
      continue;
    // Draw Item Text
    rItem.bottom = rItem.top + Theme::DetailsFooterFontSize;
    ExtTextOut(hdc, rItem.left, rItem.top, 0, NULL, itemStr, _tcslen(itemStr), 0);
    rItem.top = rItem.bottom + Theme::iDetailsFooterSpacing;
  }
}

/////////////////////////////////////
//  CListItemDetailRingtone
/////////////////////////////////////

CListItemDetailRingtone::CListItemDetailRingtone(const TCHAR* sString)
  : CListItemDetail(iptRingtone)
{
  if (sString)
    ::StringCchCopy(this->string, MAX_PATH, sString);
  else 
    this->string[0] = 0;
  this->stringLen = _tcslen(this->string);
}

int CListItemDetailRingtone::GetTextOffset()
{
  int nonTextWidth = Theme::iDetailsIndent + (Theme::iDetailsTypeWidth - Theme::iDetailIconsW) / 2;
  if (Theme::HasSmallIcons) 
    nonTextWidth += Theme::iDetailSmallIconsW + 2;
  return nonTextWidth;
}

void CListItemDetailRingtone::Prepare(HDC hdc, int w, int h)
{
  CListItemDetail::Prepare(hdc, w, h);

  SelectObject(hdc, Theme::DetailsFooterFont);
  if (this->stringLen == 0) 
  {
    ::StringCchCopy(this->string, MAX_PATH, Language::noringtone);
  }
  else
  {
    SIZE size;
    GetTextExtentPoint32(hdc, this->string, this->stringLen, &size);
    int nonTextWidth = GetTextOffset();
    if (size.cx > w - nonTextWidth)
    {
      TCHAR * p;
      TCHAR * part;
      GetTextExtentPoint32(hdc, TEXT("..."), 3, &size);
      int elen = size.cx;
      p = this->string;
      while (*p != '\0')
      {
        while ((*p != '\0') && (*p == '\\')) p++;
        part = p;
        while ((*p != '\0') && (*p != '\\')) p++;
        int len = _tcslen(p);
        if (len == 0) 
          p = part;
        else
          GetTextExtentPoint32(hdc, p, len, &size);
        if ((len == 0) || (size.cx + elen < w - nonTextWidth))
        {
          ::StringCchCopy(this->string, MAX_PATH, TEXT("..."));
          ::StringCchCat(this->string, MAX_PATH, p);
          break;
        }
      }
    }
  }
  int calcHeight = Theme::iDetailsFooterSpacing * 2 + Theme::DetailsFooterFontSize;
  if (Theme::HasSmallIcons) 
  {
    int iconPlaceHeight = Theme::iDetailSmallIconsH + 2 + 1;
    if (iconPlaceHeight > calcHeight)
      calcHeight = iconPlaceHeight;
  }
  height = calcHeight;
}

void CListItemDetailRingtone::Draw(HDC hdc, const RECT& rc) const
{
  CListItemDetail::Draw(hdc, rc);

  RECT rItem = rc;
  ::SelectObject(hdc, Theme::DetailsFooterFont);
  ::SetTextColor(hdc, Theme::rgbDetailsItemText);

  if (Theme::HasSmallIcons) 
    Theme::DrawDetailsIcon(hdc, rc.left + Theme::iDetailsIndent + (Theme::iDetailsTypeWidth - Theme::iDetailIconsW) / 2,
      rc.top + (((rc.bottom - rc.top) - Theme::iDetailSmallIconsH) / 2), diRingtone);

  rItem.left += GetTextOffset();
  DrawText(hdc, this->string, -1, &rItem, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS);
}

bool CListItemDetailRingtone::OnMouseClick(HWND hParent, int x, int y)
{
  PostCommandF(hParent, CMD_DETAILS_SET_RINGTONE, 1);
  return true;
}

bool CListItemDetailRingtone::IsLongTapValid(HWND hParent)
{
  return (stringLen != 0);
}

bool CListItemDetailRingtone::OnKeyUp(HWND hParent, int vKey)
{
  switch(vKey)
  {
  case VK_TACTION:
    PostCommandF(hParent, CMD_DETAILS_SET_RINGTONE, 1);
    return true;
  }

  return CListItemDetail::OnKeyUp(hParent, vKey);
}

bool CListItemDetailRingtone::OnLongTap(HWND hParent)
{
  //clear ringtone
  PostCommandF(hParent, CMD_DETAILS_SET_RINGTONE, 0);
  return true;
}
